Conv2DFusion

对输入 Tensor 计算二维卷积,输入的 shape 为 \((N, H_{in}, W_{in}, C_{in})\),其中 \(N\) 为 batch size,\(C\) 为通道数,\(H\) 为特征图的高度,\(W\) 为特征图的宽度。

根据以下公式计算输出:

\[out(N_i, C_{out_j}) = bias(C_{out_j}) + \sum_{k=0}^{C_{in}-1} \text{ccor}(\text{weight}(C_{out_j}, k), X(N_i, k))\]

其中,\(bias\) 为输出偏置,\(\text{ccor}\) 为 cross-correlation 操作,\(weight\) 为卷积核的值,\(X\) 为输入的特征图。

  • \(i\) 对应 batch 数,其范围为 \([0, N-1]\),其中 \(N\) 为输入 batch。

  • \(j\) 对应输出通道,其范围为 \([0, C_{out}-1]\),其中 \(C_{out}\) 为输出通道数,该值也等于卷积核的个数。

  • \(k\) 对应输入通道数,其范围为 \([0, C_{in}-1]\),其中 \(C_{in}\) 为输入通道数,该值也等于卷积核的通道数。

因此,上面的公式中,\(bias(C_{out_j})\) 为第 \(j\) 个输出通道的偏置,\(weight(C_{out_j}, k)\) 表示第 \(j\) 个卷积核在第 \(k\) 个输入通道的卷积核切片,\(X(N_i, k)\) 为特征图第 \(i\) 个 batch 第 \(k\) 个输入通道的切片。卷积核 shape 为 \((\text{kernel\_size}[0], \text{kernel\_size}[1])\),其中 kernel_size[0] 和 kernel_size[1] 是卷积核的高度和宽度。若考虑到输入输出通道以及 group,则完整卷积核的 shape 为 \((C_{out}, \text{kernel\_size}[0], \text{kernel\_size}[1], C_{in}/\text{group})\),其中 group 是分组卷积时在通道上分割输入 \(x\) 的组数。

输入:
  • input_x - 输入数据的地址

  • input_w - 输入卷积核权重的地址

  • bias - 输入偏置的地址

  • conv_param - 算子计算所需参数的结构体。其各成员见下述。

  • quant_param - 对int8类型进行量化计算所需参数的结构体(仅FT78NE需要)。其各成员见下述。

  • core_mask - 核掩码。

ConvParameter及ConvQuantParameter定义:

 1typedef struct ConvParameter {
 2    void* workspace_;      // 仅 *_p 使用;*_s 不读(见 警告)
 3    int output_batch_;     // 输出数据总批次
 4    int input_batch_;      // 输入数据总批次
 5    int input_h_;          // 输入数据h维度大小
 6    int input_w_;          // 输入数据w维度大小
 7    int output_h_;         // 输出数据h维度大小
 8    int output_w_;         // 输出数据w维度大小
 9    int input_channel_;    // 输入数据通道数
10    int output_channel_;   // 输出数据通道数
11    int kernel_h_;         // 卷积核h维度大小
12    int kernel_w_;         // 卷积核w维度大小
13    int group_;            // 组数
14    int pad_l_;            // 左填充大小
15    int pad_u_;            // 上填充大小
16    int dilation_h_;       // 卷积核h维度膨胀尺寸大小
17    int dilation_w_;       // 卷积核w维度膨胀尺寸大小
18    int stride_h_;         // 卷积核h维度步长
19    int stride_w_;         // 卷积核w维度步长
20    int buffer_size_;      // 仅 *_p:元素个数;*_s 不读。见下方警告。
21} ConvParameter;
22
23typedef struct ConvQuantParameter {  // 仅FT78NE需要
24    int32_t* left_shift_;
25    int32_t* right_shift_;
26    int32_t* multiplier_;
27    int32_t* filter_zp_ptr_;
28    int32_t output_zp_;
29    int32_t mini_;
30    int32_t maxi_;
31    int per_channel_;
32} ConvQuantParameter;
输出:
  • out_y - 输出地址。

支持平台:

FT78NE MT7004

警告

MT7004的workspace_ / buffer_size_ 分配算法:

  1. 共享版 _s 不读 workspace_ / buffer_size_。LA 写死每核 AM 0x10000000,内部 BUFFER_SIZE = 65536 元素;布局 [im2col | packed weights] 各占一半。Host 无需在 DDR 上按 buffer_size_ 申请 workspace(可填 NULL / 0)。

  2. C 参考与生产 LA 不一致:C golden 用 DDR workspace + logic_core_id × 2048;LA _s 用每核私有 AM。

  3. 私有版 _p 才读这两个字段。令 k = Kh × Kw × (Cin / G), chunk = buffer_size_ / k,须 buffer_size_ ≥ k(常用 max(2048, k))。 workspace 字节:

    • fp 私有 _p:buffer_size_ × sizeof(float)(仅 im2col)

    • hp 私有 _p:2 × buffer_size_ × sizeof(half)(im2col + 权重 pack)

  4. 推荐(_p):

    k = Kh * Kw * (Cin / G);
    buffer_size_ = max(2048, k);   /* 元素个数 */
    /* fp: */
    workspace_bytes = buffer_size_ * sizeof(float);
    /* hp: */
    workspace_bytes = 2 * buffer_size_ * sizeof(half);
    

备注

  • FT78NE 支持int8(需额外传入 ConvQuantParameter 量化参数), fp32

  • MT7004 支持fp16, fp32

共享存储版本:

void i8_conv_s(int8_t *input_x, int8_t *input_w, int8_t *out_y, int *bias, ConvParameter *conv_param, ConvQuantParameter *quant_param, int core_mask)
void hp_conv_s(half *input_x, half *input_w, half *out_y, half *bias, ConvParameter *conv_param, int core_mask)
void fp_conv_s(float *input_x, float *input_w, float *out_y, float *bias, ConvParameter *conv_param, int core_mask)

C调用示例:

 1void TestConvSMCFp32(int* input_shape, int* weight_shape, int* output_shape, int* stride, int* padding, int* dilation, int groups, float* bias, int core_mask) {
 2    int core_id = get_core_id();
 3    int logic_core_id = GetLogicCoreId(core_mask, core_id);
 4    int core_num = GetCoreNum(core_mask);
 5    float* input_data = (float*)0x88000000;
 6    float* weight = (float*)0x89000000;
 7    float* output_data = (float*)0x90000000;
 8    float* bias_data = (float*)0x91000000;
 9    ConvParameter* param = (ConvParameter*)0x92000000;
10    if (logic_core_id == 0) {
11        memcpy(bias_data, bias, sizeof(float) * output_shape[3]);
12        param->dilation_h_ = dilation[0];
13        param->dilation_w_ = dilation[1];
14        param->group_ = groups;
15        param->input_batch_ = input_shape[0];
16        param->input_h_ = input_shape[1];
17        param->input_w_ = input_shape[2];
18        param->input_channel_ = input_shape[3];
19        param->kernel_h_ = weight_shape[1];
20        param->kernel_w_ = weight_shape[2];
21        param->output_batch_ = output_shape[0];
22        param->output_h_ = output_shape[1];
23        param->output_w_ = output_shape[2];
24        param->output_channel_ = output_shape[3];
25        param->stride_h_ = stride[0];
26        param->stride_w_ = stride[1];
27        param->pad_u_ = padding[0];
28        param->pad_l_ = padding[2];
29        /* *_s 不读这两个字段;LA 使用每核 AM 0x10000000,BUFFER_SIZE=65536 */
30        param->workspace_ = NULL;
31        param->buffer_size_ = 0;
32    }
33    sys_bar(0, core_num); // 初始化参数完成后进行同步
34    fp_conv_s(input_data, weight, output_data, bias_data, param, core_mask);
35}
36
37void main(){
38    int in_channel = 4;
39    int out_channel = 4;
40    int groups = 4;
41    int input_shape[4] = {1, 30, 30, in_channel}; // NHWC
42    int weight_shape[4] = {out_channel, 3, 3, in_channel / groups};
43    int output_shape[4] = {1, 10, 10, out_channel}; // NHWC
44    int stride[2] = {2, 2};
45    int padding[4] = {1, 1, 1, 1};
46    int dilation[2]= {2, 2};
47    float bias[4] = {0, 0, 0, 0};
48    int core_mask = 0b1111;
49    TestConvSMCFp32(input_shape, weight_shape, output_shape, stride, padding, dilation, groups, bias, core_mask);
50}

私有存储版本:

void i8_conv_p(int8_t *input_x, int8_t *input_w, int8_t *out_y, int *bias, ConvParameter *conv_param, ConvQuantParameter *quant_param, int core_mask)
void hp_conv_p(half *input_x, half *input_w, half *out_y, half *bias, ConvParameter *conv_param, int core_mask)
void fp_conv_p(float *input_x, float *input_w, float *out_y, float *bias, ConvParameter *conv_param, int core_mask)

C调用示例:

 1void TestConvL2Fp32(int* input_shape, int* weight_shape, int* output_shape, int* stride, int* padding, int* dilation, int groups, float* bias, int core_mask) {
 2    float* input_data = (float*)0x10010000; // 私有存储版本地址设置在AM内
 3    float* weight = (float*)0x10020000;
 4    float* output_data = (float*)0x10030000;
 5    float* bias_data = (float*)0x10040000;
 6    ConvParameter* param = (ConvParameter*)0x10060000;
 7    memcpy(bias_data, bias, sizeof(float) * output_shape[3]);
 8    param->dilation_h_ = dilation[0];
 9    param->dilation_w_ = dilation[1];
10    param->group_ = groups;
11    param->input_batch_ = input_shape[0];
12    param->input_h_ = input_shape[1];
13    param->input_w_ = input_shape[2];
14    param->input_channel_ = input_shape[3];
15    param->kernel_h_ = weight_shape[1];
16    param->kernel_w_ = weight_shape[2];
17    param->output_batch_ = output_shape[0];
18    param->output_h_ = output_shape[1];
19    param->output_w_ = output_shape[2];
20    param->output_channel_ = output_shape[3];
21    param->stride_h_ = stride[0];
22    param->stride_w_ = stride[1];
23    param->pad_u_ = padding[0];
24    param->pad_l_ = padding[2];
25    /* *_p:buffer_size_ 元素个数,须 >= Kh*Kw*(Cin/G);FP workspace = 1*buffer_size_ */
26    param->workspace_ = (float*)0x10070000;
27    param->buffer_size_ = 2048;
28    fp_conv_p(input_data, weight, output_data, bias_data, param, core_mask);
29}
30
31void main(){
32    int in_channel = 4;
33    int out_channel = 4;
34    int groups = 4;
35    int input_shape[4] = {1, 30, 30, in_channel}; // NHWC
36    int weight_shape[4] = {out_channel, 3, 3, in_channel / groups};
37    int output_shape[4] = {1, 10, 10, out_channel}; // NHWC
38    int stride[2] = {2, 2};
39    int padding[4] = {1, 1, 1, 1};
40    int dilation[2]= {2, 2};
41    float bias[4] = {0, 0, 0, 0};
42    int core_mask = 0b0001; // 私有存储版本只能设置为一个核心启动
43    TestConvL2Fp32(input_shape, weight_shape, output_shape, stride, padding, dilation, groups, bias, core_mask);
44}